# -*- python -*-
# ex: set syntax=python:

# This is a sample buildmaster config file. It must be installed as
# "master.cfg" in your buildmaster's base directory.

import os.path
import re

# This is the dictionary that the buildmaster pays attention to. We
# also use a shorter alias to save typing.
c = BuildmasterConfig = {}

####### BUILDSLAVES

# The "slaves" list defines the set of recognized buildslaves. Each
# element is a BuildSlave object, specifying a unique slave name and
# password.  The same slave name and password must be configured on
# the slave.
from buildbot.buildslave import BuildSlave

try:
    from secret_passwords import buildslave_passwords, webauth_passwords, \
            schedulers_to_slaves_and_envs
except ImportError:
    raise SystemExit("Fatal error: Is secret_passwords.py missing?\n"
                     "See code comments.")
webauth_passwords = webauth_passwords.items()

SLAVES = sorted(buildslave_passwords.keys())

c["slaves"] = [BuildSlave(slave, buildslave_passwords[slave]) for slave in SLAVES]

# "slavePortnum" defines the TCP port to listen on for connections from slaves.
# This must match the value configured into the buildslaves (with their
# --master option)
c["slavePortnum"] = 9989

####### CHANGESOURCES

# the "change_source" setting tells the buildmaster how it should find out
# about source code changes.

from buildbot.changes.pb import PBChangeSource
c["change_source"] = PBChangeSource(port="tcp:9987:interface=127.0.0.1")

####### SCHEDULERS

# Configure the Schedulers, which decide how to react to incoming
# changes.

from buildbot.schedulers.basic import SingleBranchScheduler, Dependent
from buildbot.schedulers.timed import Nightly
from buildbot.schedulers.forcesched import ForceScheduler
from buildbot.changes import filter

c["schedulers"] = []

def add_scheduler(name, cls, **kwargs):
    builder_names = ["%s-%s-%s" % (name, slave_name, env_nick)
                     for slave_name, (env_nick, env) in schedulers_to_slaves_and_envs[name]]
    if cls is SingleBranchScheduler and 'change_filter' not in kwargs:
        kwargs['change_filter'] = filter.ChangeFilter(branch="default")
    scheduler = cls(
            name=name,
            builderNames=builder_names,
            **kwargs)
    c["schedulers"].append(scheduler)
    force_scheduler = ForceScheduler(
        name="force-%s" % name,
        builderNames=builder_names)
    c["schedulers"].append(force_scheduler)
    return scheduler

# Scheduler autodoc: run after successful quick builds.
style_checks = add_scheduler(name="style-checks",
                             cls=SingleBranchScheduler,
                             treeStableTimer=None)

# Schedulers for builds that run after every change.
linux_build = add_scheduler(name="linux-build",
                            cls=Dependent,
                            upstream=style_checks)

add_scheduler(name="linux64-build",
              cls=Dependent,
              upstream=linux_build)

add_scheduler(name="vc12-build",
              cls=Dependent,
              upstream=style_checks)
add_scheduler(name="vc14-build",
              cls=Dependent,
              upstream=style_checks)
add_scheduler(name="mac-build",
              cls=Dependent,
              upstream=style_checks)

# Scheduler for a test that runs every night at 3:00 AM.
add_scheduler(name="nightly",
              cls=Nightly,
              branch="default",
              hour=3,
              minute=0,
              onlyIfChanged=True)

# Scheduler for a test that runs every Saturday at 8:00 AM.
add_scheduler(name="weekly",
              cls=Nightly,
              branch="default",
              dayOfWeek=5,
              hour=8,
              minute=0,
              onlyIfChanged=True)

# Scheduler autodoc: run after successful quick builds.
add_scheduler(name="autodoc",
              cls=Dependent,
              upstream=linux_build)

####### BUILDERS

# The "builders" list defines the Builders, which tell Buildbot how to
# perform a build: what steps, and which slaves can execute them.
# Note that any particular build will only take place on one slave.

from buildbot.process.factory import BuildFactory
from buildbot.steps.source.mercurial import Mercurial
from buildbot.steps.shell import Compile, ShellCommand

REPO_URL = "ssh://ai-repos/ai/downward"
HTTP_REPO_URL = "http://hg.fast-downward.org"
BENCHMARKS_DIR = "/home/linuxbuildslave/lib/downward-benchmarks"

incremental_update_step = Mercurial(
    name="incremental_update",
    repourl=REPO_URL,
    mode="incremental",
    branchType="inrepo",
    haltOnFailure=True,
    warnOnWarnings=True)

incremental_update_step_http = Mercurial(
    name="incremental_update",
    repourl=HTTP_REPO_URL,
    mode="incremental",
    branchType="inrepo",
    haltOnFailure=True,
    warnOnWarnings=True)

clean_update_step = Mercurial(
    name="clean_update",
    repourl=REPO_URL,
    mode="full",
    method="clobber",
    branchType="inrepo",
    haltOnFailure=True,
    warnOnWarnings=True)

clean_update_step_http = Mercurial(
    name="clean_update",
    repourl=HTTP_REPO_URL,
    mode="full",
    method="clobber",
    branchType="inrepo",
    haltOnFailure=True,
    warnOnWarnings=True)

build_release_step = Compile(
    name="build_release",
    command=["python", "./build.py"],
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

build_release64_step = Compile(
    name="build_debug",
    command=["python", "./build.py", "release64"],
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

build_debug_step = Compile(
    name="build_debug",
    command=["python", "./build.py", "debug32"],
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

build_debug64_step = Compile(
    name="build_debug",
    command=["python", "./build.py", "debug64"],
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

nightly_planner_step = ShellCommand(
    name="nightly_planner",
    command=["python", "./buildbot-exp.py", "--test", "nightly", "--all"],
    workdir="build/misc/buildbot",
    description=["running", "planner", "tests"],
    descriptionDone=["planner", "tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

weekly_planner_step = ShellCommand(
    name="weekly_planner",
    command=["python", "./buildbot-exp.py", "--test", "weekly", "--all"],
    workdir="build/misc/buildbot",
    description=["running", "planner", "tests"],
    descriptionDone=["planner", "tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

medium_translator_step = ShellCommand(
    name="medium_translator",
    command=["python", "./test-translator.py", BENCHMARKS_DIR, "first"],
    workdir="build/misc/tests",
    description=["running", "translator", "tests"],
    descriptionDone=["translator", "tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

autodoc_step = ShellCommand(
    name="autodoc",
    command=["python", "./autodoc.py"],
    workdir="build/misc/autodoc",
    description=["running", "autodoc"],
    descriptionDone=["autodoc"],
    flunkOnFailure=True,
    warnOnWarnings=True)

code_tests_step = ShellCommand(
    name="code_tests",
    command=["./run-all-code-tests"],
    workdir="build/misc/tests",
    description=["running", "code", "tests"],
    descriptionDone=["code_tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

style_step = ShellCommand(
    name="style",
    command=["python", "./run-all-style-checks.py"],
    workdir="build/misc/style",
    description=["checking", "style"],
    descriptionDone=["style"],
    flunkOnFailure=True,
    warnOnWarnings=True)

# These steps are currently only used on Windows, because we do
# not test the translator there, and hence cannot use code_tests_step.
test_exitcodes_step = ShellCommand(
    name="exitcode_tests",
    command=["python", "./test-exitcodes.py"],
    workdir="build/misc/tests",
    description=["running", "exitcode", "tests"],
    descriptionDone=["exitcode_tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

test_standard_configs_step = ShellCommand(
    name="standard_configs_tests",
    command=["python", "./test-standard-configs.py"],
    workdir="build/misc/tests",
    description=["running", "standard", "configs", "tests"],
    descriptionDone=["standard_configs_tests"],
    flunkOnFailure=True,
    warnOnWarnings=True)

# Windows part
VC12_COMPILER_SETUP_COMMAND = '"C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat" x86'
VC14_COMPILER_SETUP_COMMAND = '"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat" x86'

vc12_build_release_step = Compile(
    name="build_release",
    command=VC12_COMPILER_SETUP_COMMAND + " & python build.py release32",
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

vc12_build_debug_step = Compile(
    name="build_debug",
    command=VC12_COMPILER_SETUP_COMMAND + " & python build.py debug32",
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

vc14_build_release_step = Compile(
    name="build_release",
    command=VC14_COMPILER_SETUP_COMMAND + " & python build.py release32nolp",
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)

vc14_build_debug_step = Compile(
    name="build_debug",
    command=VC14_COMPILER_SETUP_COMMAND + " & python build.py debug32nolp",
    workdir="build",
    haltOnFailure=True,
    warnOnWarnings=True)


factory_dict = {}

factory_dict["style-checks"] = BuildFactory([
        incremental_update_step,
        style_step,
        ])

factory_dict["linux-build"] = BuildFactory([
        incremental_update_step,
        build_release_step,
        clean_update_step,
        build_release_step,
        build_debug_step,
        code_tests_step,
        ])

factory_dict["linux64-build"] = BuildFactory([
        incremental_update_step,
        build_release64_step,
        build_debug64_step,
        ])

factory_dict["nightly"] = BuildFactory([
        clean_update_step,
        build_release_step,
        nightly_planner_step,
        medium_translator_step,
        ])

factory_dict["weekly"] = BuildFactory([
        clean_update_step,
        build_release_step,
        weekly_planner_step,
        medium_translator_step,
        ])

factory_dict["autodoc"] = BuildFactory([
        incremental_update_step,
        autodoc_step,
])
factory_dict["autodoc"].useProgress = False

# Windows part
factory_dict["vc12-build"] = BuildFactory([
        incremental_update_step_http,
        vc12_build_release_step,
        clean_update_step_http,
        vc12_build_release_step,
        vc12_build_debug_step,
        test_exitcodes_step,
        test_standard_configs_step,
        ])

factory_dict["vc14-build"] = BuildFactory([
        incremental_update_step_http,
        vc14_build_release_step,
        clean_update_step_http,
        vc14_build_release_step,
        vc14_build_debug_step,
        ])

# OS X part
factory_dict["mac-build"] = BuildFactory([
        incremental_update_step_http,
        build_release_step,
        clean_update_step_http,
        build_release_step,
        build_debug_step,
        test_exitcodes_step,
        test_standard_configs_step,
        ])

from buildbot import locks
from buildbot.config import BuilderConfig

# By default, only allow one concurrent build on each slave. In the
# future, we could use the maxCountForSlave kwarg to raise the number of
# concurrent builds for certain slaves.
build_lock = locks.SlaveLock("slave_builds", maxCount = 1)

c["builders"] = []
for scheduler, slaves_and_envs in sorted(schedulers_to_slaves_and_envs.items()):
    for slave, (env_nick, env) in slaves_and_envs:
            c["builders"].append(BuilderConfig(
                    name="%s-%s-%s" % (scheduler, slave, env_nick),
                    slavename=slave,
                    factory=factory_dict[scheduler],
                    mergeRequests=False,
                    env=env,
                    locks=[build_lock.access("counting")]))


####### STATUS TARGETS

# "status" is a list of Status Targets. The results of each build will be
# pushed to these targets. buildbot/status/*.py has a variety to choose from,
# including web pages, email senders, and IRC bots.

c["status"] = []

from buildbot.status.html import WebStatus
from buildbot.status.web.auth import BasicAuth
from buildbot.status.web.authz import Authz

authz_cfg = Authz(
    auth=BasicAuth(webauth_passwords),
    gracefulShutdown="auth",
    forceBuild="auth",
    forceAllBuilds="auth",
    pingBuilder="auth",
    stopBuild="auth",
    stopAllBuilds="auth",
    cancelPendingBuild="auth",
)

c["status"].append(WebStatus(
        http_port="tcp:8010:interface=127.0.0.1",
        authz=authz_cfg))


from zope.interface import implements

from buildbot import interfaces
from buildbot.status.mail import MailNotifier

class ParseEmail(object):
    implements(interfaces.IEmailLookup)
    def getAddress(self, name):
        """John Doe <john.doe@abc.com> --> john.doe@abc.com"""
        match = re.match(r".*<(.+@.+)>\s*$", name)
        if match:
            return match.group(1)
        return None

c["status"].append(MailNotifier(
        fromaddr="malte.helmert@unibas.ch",
        sendToInterestedUsers=True,
        extraRecipients=["malte.helmert@unibas.ch"],
        mode="warnings",
        lookup=ParseEmail()))

####### PROJECT IDENTITY

# the "title" string will appear at the top of this buildbot
# installation's html.WebStatus home page (linked to the
# "titleURL") and is embedded in the title of the waterfall HTML page.

c["title"] = "Fast Downward"
c["titleURL"] = "http://www.fast-downward.org/"

# the "buildbotURL" string should point to the location where the buildbot's
# internal web server (usually the html.WebStatus page) is visible. This
# typically uses the port number set in the Waterfall "status" entry, but
# with an externally-visible host name which the buildbot cannot figure out
# without some help.

c["buildbotURL"] = "http://buildbot.fast-downward.org/"

####### DB URL

# This specifies what database buildbot uses to store change and scheduler
# state.  You can leave this at its default for all but the largest
# installations.
c["db_url"] = "sqlite:///state.sqlite"
